#ifndef _UDP_SERVER_HPP
#define _UDP_SERVER_HPP

#include "Log.hpp"
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SIZE 1024

class UdpServer
{
public:
    UdpServer(uint16_t port, std::string ip = "")
        : _port(port)
        , _ip(ip)
    {}

    void InitServer()
    {
        // 1. 创建套接字
        // 前两个参数已经能确定是是UDP的网络通信了,第三个参数设置为0即可
        _socket = socket(AF_INET, SOCK_DGRAM, 0);
        if(_socket < 0)
        {
            logMessage(FATAL, "errno:%d, strerror", errno, strerror(errno));
            exit(2);
        }

        // 2. 绑定端口号(将用户设置的ip和端口号与当前的进程强绑定)
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        // 服务器的IP地址和端口号是要发给对方主机的,首先要发送到网络
        local.sin_port = htons(_port);
        // 点分十进制的IP地址: "192.168.110.132"
        // 每一个区域取值范围是[0-255]: 1字节 -> 4个区域
        // 理论上,表示一个IP地址,其实4字节就够了
        // 需要将点分十进制字符串风格的IP地址转成4字节的二进制序列
        // 4字节的二进制序列,还需要从主机序列转为网络序列
        // 以上过程可以通过inet_addr函数来完成
        // sin_addr.s_addr是4字节的二进制网络序列
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        if(bind(_socket, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "errno:%d, strerror", errno, strerror(errno));
            exit(3);
        }
        logMessage(NORMAL, "Init UdpServer Success!");
    }

    void StartServer()
    {
        // 网络服务器是常驻进程,永远不会退出,除非挂掉了
        char buffer[SIZE];
        while(true)
        {
            struct sockaddr_in peer;   // 输出型参数
            bzero(&peer, sizeof(peer)); // 将比特位全部置为0
            socklen_t len = sizeof(peer);  // 输入输出型参数

            char ret[256];
            std::string cmdRet; // 命令的执行结果
            ssize_t s = recvfrom(_socket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if(s > 0)
            {
                buffer[s] = '\0';
                // 过滤危险指令
                if(strcasestr(buffer, "rm") != nullptr || strcasestr(buffer, "rmdir") != nullptr)
                {
                    std::string errMessage = "哎呦,你干嘛！";
                    std::cout << errMessage << buffer << std::endl;
                    sendto(_socket, errMessage.c_str(), errMessage.size(), 0, (struct sockaddr*)&peer, len);
                    continue; 
                }

                // buffer中的内容看做指令
                FILE* fp = popen(buffer, "r");
                if(fp == nullptr) // 打开文件失败
                {
                    logMessage(ERROR, "errno:%d strerror:%s\n", errno, strerror(errno));
                    continue;
                }
                // 从文件中读取指令执行结果
                while(fgets(ret, sizeof(ret), fp) != nullptr)
                {
                    cmdRet += ret;
                }
                fclose(fp);

                uint16_t clientPort = ntohs(peer.sin_port);
                std::string clientIP = inet_ntoa(peer.sin_addr);
                printf("clientIP:%s clientPort:%d# %s\n", clientIP.c_str(), clientPort, buffer);
            }
            // 将指令的执行结果发送给客户端
            sendto(_socket, cmdRet.c_str(), cmdRet.size(), 0, (struct sockaddr*)&peer, len);
        }
    }

    ~UdpServer()
    {
        if(_socket >= 0) close(_socket);
    }

private:
    uint16_t _port;
    std::string _ip;
    int _socket;
};

#endif